package controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import model.Bag;
import model.Dictionary;
import model.Player;
import model.Rack;
import model.ScrabbleGame;
import model.Tile;
import model.iview.ITile;
import model.listeners.PlayerListener;
import model.listeners.TurnListener;
import model.swing.MoveListModel;
import model.swing.PlayersTableModel;
import util.Coordinate;
import util.Resources;
import view.IBoardView;
import view.ILogView;
import view.IPlayersView;
import view.IRackView;
import view.swing.BoardView;
import view.swing.LogView;
import view.swing.MainWindow;
import view.swing.PlayersView;
import view.swing.RackView;
import view.swing.popup.EndGameWindow;

/**
 * 
 * @author Lucile Gramusset et Guillaume Poussel
 * 
 */
public class GameController extends AbstractController {

    private ScrabbleGame scrabbleGame;

    private BoardController boardController;
    private PlayerController playerController;
    private RackController rackController;
    private ActionController actionController;
    private LogController logController;

    private MainWindow mainWindow;

    /**
     * Crée un contrôleur de jeu
     * 
     * @param sg
     *            Modèle du jeu de scrabble
     */
    public GameController() {
        super(null);
    }

    /**
     * Crée tous les contrôleurs nécessaires à l'application
     */
    public void init(IBoardView boardView, ILogView logView,
            IPlayersView playersView, IRackView rackView) {
        boardController = new BoardController(this, boardView);

        logController = new LogController(this, logView);

        playerController = new PlayerController(this, playersView);

        rackController = new RackController(this, rackView);

        actionController = new ActionController(this);

    }

    /**
     * @return Contrôleur du plateau
     */
    public BoardController getBoardController() {
        return boardController;
    }

    /**
     * @return Contrôleur des joueurs
     */
    public PlayerController getPlayerController() {
        return playerController;
    }

    /**
     * @return Contrôleur du rack
     */
    public RackController getRackController() {
        return rackController;
    }

    /**
     * @return Contrôleur des actions (jouer, passer...)
     */
    public ActionController getActionController() {
        return actionController;
    }

    /**
     * @return Contrôleur des logs
     */
    public LogController getLogController() {
        return logController;
    }

    /**
     * @return Fenêtre principale du jeu
     */
    protected MainWindow getMainWindow() {
        return mainWindow;
    }

    /**
     * Joue un coup
     * 
     * @param tiles
     *            Les tuiles posées par l'utilisateur
     */
    public void playTurn(HashMap<Coordinate, Tile> tiles) {
        boolean retour = scrabbleGame.play(tiles);
        if (retour) {
            getBoardController().fixAllTiles();
            scrabbleGame.nextPlayer();
        } else {
            getBoardController().getView().resetJokers();
        }
    }

    /**
     * Ajoute un "listener" sur le joueur en cours
     * 
     * @param pl
     *            Le "listener" à ajouter
     */
    public void addPlayerListener(PlayerListener pl) {
        this.scrabbleGame.addPlayerListener(pl);
    }

    /**
     * Passe son tour en comptant le changement comme un "passe"
     */
    public void skipTurn() {
        skipTurn(true);
    }

    /**
     * Passe son tour
     * 
     * @param reallySkip
     *            Compter ou non le changement comme un "passe"
     */
    public void skipTurn(boolean reallySkip) {
        scrabbleGame.skipTurn(reallySkip);
    }

    /**
     * Echange des tuiles
     * 
     * @param choice
     *            Tuiles à échanger
     */
    public void swap(List<Tile> choice) {
        scrabbleGame.swapTiles(choice);
    }

    /**
     * Ajouter un "listener" sur les tours joués
     * 
     * @param tl
     *            Le "listener" à ajouter
     */
    public void addTurnListener(TurnListener tl) {
        this.scrabbleGame.addTurnListener(tl);
    }

    /**
     * Vérifie un mot dans le dictionnaire
     * 
     * @param word
     *            Le mot à vérifier
     * @return <code>true</code> si le mot est dans le dictionnaire,
     *         <code>false</code> sinon.
     */
    public boolean checkWord(String word) {
        return Dictionary.getInstance().contains(word);
    }

    /**
     * Termine le jeu, et affiche la fenêtre de fin
     */
    public void endGame() {
        scrabbleGame.endGame();
        EndGameWindow egw = new EndGameWindow(mainWindow, scrabbleGame
                .getIPlayers());
        egw.showWindow();
        System.exit(0);
    }

    /**
     * Récupère le rack en cours
     * 
     * @return Le rack du joueur en cours
     */
    public Rack getCurrentRack() {
        return this.scrabbleGame.getCurrentPlayer().getRack();
    }

    public void initGame(ArrayList<String> names, int nbPlayers) {
        scrabbleGame = new ScrabbleGame();
        for (String name : names) {
            scrabbleGame.addPlayer(new Player(name));
        }
        if (names.size() < nbPlayers) {
            for (int i = names.size(); i < nbPlayers; i++) {
                scrabbleGame.addPlayer(new Player(Resources.getString(
                        "Player.DefaultName", i+1)));
            }
        }

        IBoardView boardView = new BoardView(scrabbleGame.getBoard());

        MoveListModel listModel = new MoveListModel();
        ILogView logView = new LogView(listModel);
        scrabbleGame.addTurnListener(logView);
        scrabbleGame.addTurnListener(listModel);

        PlayersTableModel tableModel = new PlayersTableModel(scrabbleGame
                .getPlayers());
        IPlayersView playersView = new PlayersView(tableModel);
        for (Player player : scrabbleGame.getPlayers()) {
            player.addScoreListener(playersView);
        }

        scrabbleGame.addPlayerListener(playersView);
        scrabbleGame.addPlayerListener(tableModel);
        scrabbleGame.firePlayerListeners();

        scrabbleGame.start();
        IRackView rackView = new RackView(scrabbleGame.getCurrentPlayer()
                .getRack());
        scrabbleGame.addRackListener(rackView);

        this.init(boardView, logView, playersView, rackView);

        mainWindow = new MainWindow(this, boardView, playersView, logView,
                rackView);
        scrabbleGame.addErrorListener(mainWindow);
    }

    public Map<ITile, Integer> getBagDistribution() {
        return Bag.getDistribution();
    }

}
